home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1999 July / Macworld (1999-07).dmg / MPEG 3 Utilities / MacAMP 1.0b7 / Visual Plugins Programming / LevelMeter++ / Source / main.c < prev    next >
Text File  |  1999-01-30  |  10KB  |  417 lines

  1. /*
  2.     LevelMeter Visual Plugin Example
  3.     
  4.             Sample visual plugin to demonstrate MacAmp visual plugin API and additional tricks,
  5.             including access of the plugin resource fork, initializing the plugin info structure on
  6.             startup and more.
  7.             
  8.             You can use any portions of this code freely in your plugins without any restrictions.
  9.  
  10.     By: Slava Karpenko (slava@macamp.com)    9/2/98
  11.         @soft
  12.         
  13. */
  14.  
  15. #include "visual.h"
  16.  
  17. // Some useful defines.
  18. #define MIN(a,b)    (((a)<(b))?(a):(b))
  19. #define MAX(a,b)    (((a)>(b))?(a):(b))
  20. #define ABS(a)        ((a)<0?-(a):(a))
  21.  
  22. // This is the gPlugInfo block - a place where MacAmp gets initial information about your plugin.
  23. // Make sure to set the fragment's entry point to gPlugInfo to make it accessible for the MacAmp routines.
  24.  
  25. VPInfoBlock gPlugInfo =
  26. {
  27.     VP_EXT_API_VERSION,            // Set to VP_EXT_API_VERSION
  28.     plugVisual,                    // Should be always plugVisual (otherwise what are we doing here? =)
  29.     
  30.     nil,                        // We have no flags
  31.     nil,                        // We have no global refcon.
  32.     "\pLevelMeter++",            // What's our name to display in the Plugins menu?
  33.     
  34.                                 // Function pointers. Use nil if you don't use something.
  35.     PlugInitialize,
  36.     PlugTerminate,
  37.     PlugProcess,
  38.     PlugMacEvent,
  39.     PlugKeyDown,        // (unused in this example)
  40.     PlugIdle,            // (unused in this example)
  41.     PlugSettings,        // (unused in this example)
  42.     PlugAbout,
  43.     
  44.     // • NEW FOR 1.2 •
  45.     PlugSongStart,
  46.     PlugSongEnd,
  47.     
  48.     nil                    // MacAMP will fill out this value for us.
  49. };
  50.  
  51. // These are globals.
  52. static long ticks;        // tick count variable to keep some sort of synch.
  53. static short left;        // left and right channel values (0<=x<=20)
  54. static short right;
  55. static short refnum;    // plugin resource fork refnum
  56.  
  57. // dirty hack.
  58. QDGlobalsPtr    qdPtr = nil;
  59.  
  60.  
  61. // Other function prototypes
  62. void ProcessMP3(short chan, const double* stream);
  63. void ProcessMP2(short chan, const double* stream);
  64. void DrawMeters(WindowPtr wind);
  65. OSErr PlugLoadMyself(void);
  66. void PlugUnloadMyself(void);
  67.  
  68. /* 
  69.     PlugLoadMyself
  70.  
  71.     Called when your plugin fragment is loaded in memory. You may wish to change gPlugInfo fields here, or
  72.     do something else like opening your global preferences. Take a look at the PPC Linker panel in the target settings
  73.     to get an idea how it's done.
  74.     
  75. */
  76. OSErr PlugLoadMyself(void)
  77. {
  78.     return noErr;
  79. }
  80.  
  81. /* 
  82.     PlugUnloadMyself
  83.  
  84.     Called when MacAmp is about to quit and dispose all plugins. Dispose any global stuff you allocated in PlugLoadMyself
  85.     etc.
  86.     
  87. */
  88. void PlugUnloadMyself(void)
  89. {
  90.     return;
  91. }
  92.  
  93. /* 
  94.     PlugInitialize
  95.  
  96.     Called every time use selects your plugin from the Plugins menu. You need to allocate and display a window here,
  97.     init your variables, and do whatever you want, like set the refnum if you need it.
  98.     
  99.     This function uses FSSpec to itself that MacAmp provides to it to gain access to its resource fork.
  100.     
  101.     Other than that, everything is hard-coded and is definitly not an example of good programming =P
  102. */
  103. OSErr    PlugInitialize(WindowPtr* window, FSSpecPtr file, UInt32*)
  104. {
  105.     Rect bounds = { 40, 10, 100, 19 };    // guess what this is, it's window coordinates. Yuck. =)
  106.     GrafPtr    port;
  107.     
  108.     GetPort(&port);
  109.     
  110.     // create a new window with floating look as included in MacOS 7.6+ (WDEF id 124)
  111.     *window = NewCWindow(nil,&bounds,"\p",true,1985,(WindowPtr)-1,true,nil);
  112.     
  113.     if (!*window)
  114.         return visNoMemory;
  115.     
  116.     SetPort(*window);
  117.     BackColor(blackColor);
  118.     PenSize(4, 1);
  119.     SetPort(port);
  120.  
  121.     // init our variables.
  122.     left = right = 0;
  123.     ticks = TickCount();
  124.  
  125.     // draw zeroes initially
  126.     DrawMeters(*window);
  127.  
  128.     // Open our resource fork and save the refnum for closing it (we are a well-behaved plugin, right? ;)
  129.     refnum = FSpOpenResFile(file, fsRdPerm);
  130.  
  131.     // Get qd globals from the application by calling the callback
  132.     if (gPlugInfo.callbacks != nil)
  133.         qdPtr = gPlugInfo.callbacks->getQDPtrProc();
  134.         
  135.     return visNoErr;
  136. }
  137.  
  138. /* 
  139.     PlugTerminate
  140.  
  141.     Called when user deselects your plugin, MacAmp quits or in the case if you have passed visTerminate/visNoMemory error.
  142.     You should dispose your window here & clean up the mess.
  143. */
  144. OSErr    PlugTerminate(WindowPtr window, UInt32*)
  145. {
  146.     if (window != nil)
  147.         DisposeWindow(window);
  148.     
  149.     // Close our resource fork.
  150.     if (refnum != -1)
  151.         FSClose(refnum);
  152.         
  153.     return visNoErr;
  154. }
  155.  
  156. /* 
  157.     PlugProcess
  158.  
  159.     The heart of the visual plugin. It means your plugin needs to analyze the stream and display whatever you want.
  160.     
  161.     chan is the number of channels.
  162.     stream is the sound data, which has the size of [2][32][dataSize]. For now dataSize==18 means layer III files,
  163.     dataSize == 36 means layer II files. Sound data is separated by channels, so stream[0] would be left, and stream[1] -
  164.     right channel.
  165.     
  166.     You should never ever modify the contents of the stream pointer. Doing so will affect the playback.
  167.     [On the other hand, if you know what you're doing, you CAN modify the data stream. Doing so will change the sound.
  168.     So with a little knowledge you can turn a visual plugin into a sound plugin (make a new equalizer ;)]
  169. */
  170. OSErr    PlugProcess(WindowPtr window, short chan, const double* stream, short dataSize, UInt32*)
  171. {
  172.     if (TickCount() - ticks > 1)
  173.     {
  174.         if (dataSize == 18)
  175.             ProcessMP3(chan, stream);
  176.         else
  177.             ProcessMP2(chan, stream);
  178.             
  179.         DrawMeters(window);
  180.         
  181.         left--;
  182.         right--;
  183.         ticks = TickCount();
  184.     }
  185.     
  186.     return visNoErr;
  187. }
  188.  
  189. /* 
  190.     PlugMacEvent
  191.  
  192.     Process a mac event that is related to the window (activate/deactivate, update, mouseDown).
  193.     
  194.     You can do whatever you want, but remember that if you don't handle update events, that will screw up the whole
  195.     MacAmp window update process.
  196. */
  197. OSErr    PlugMacEvent(WindowPtr window, EventRecord* event, UInt32*)
  198. {
  199.     OSErr    result = visNoErr;
  200.     switch (event->what)
  201.     {
  202.         case updateEvt:
  203.             BeginUpdate(window);
  204.             DrawMeters(window);
  205.             EndUpdate(window);
  206.             break;
  207.             
  208.         case mouseDown:
  209.             {
  210.                 WindowPtr    wind;
  211.                 short thePart = FindWindow(event->where, &wind);
  212.                 switch (thePart)
  213.                 {
  214.                     case inDrag:
  215.                         // ugly hack. it shoulkd be qd.screenBits.bounds.
  216.                         DragWindow(wind, event->where, &qdPtr->screenBits.bounds);
  217.                         break;
  218.                     
  219.                     case inGoAway:
  220.                         if (TrackGoAway(wind, event->where))
  221.                             result = visTerminate;            // if user clicked the close box, shut ourself down.
  222.                         break;
  223.                     
  224.                     case inContent:
  225.                         // hehe, max out everything
  226.                         left = right = 20;
  227.                         DrawMeters(wind);
  228.                         break;
  229.                 }
  230.                 
  231.             }
  232.             break;
  233.             
  234.         default:
  235.             break;
  236.     }
  237.     return result;
  238. }
  239.  
  240. /* 
  241.     PlugAbout
  242.  
  243.     Called when a user selects "About the Plugin" from the menu. You should display some sort of about box, 
  244.     do some sort of an effect or whatever you feel like it.
  245. */
  246. OSErr    PlugAbout(WindowPtr, UInt32*)
  247. {
  248.     NoteAlert(1234, nil);
  249.     return visNoErr;
  250. }
  251.  
  252. /* 
  253.     PlugIdle
  254.  
  255.     Called whenever MacAmp gets some spare time. It's a good place to decrement your values, draw the stuff etc.
  256.     
  257.     Called only if flagGetIdle is set.
  258. */
  259. OSErr    PlugIdle(WindowPtr window, UInt32* refcon)
  260. {
  261. #pragma unused (window, refcon)
  262.     return visNoErr;
  263. }
  264.  
  265. /* 
  266.     PlugKeyDown
  267.  
  268.     Called when a user presses a button, and after it was processed by MacAmp. Keep in mind that no matter if it
  269.     was processed by MacAmp or not, your plug still gets it. So if you want tto have some keyboard control, try to
  270.     use unused keys.
  271.     
  272.     Called only if flagGetKeyDown is set.
  273. */
  274. OSErr    PlugKeyDown(WindowPtr window, char key, short modifiers, UInt32* refcon)
  275. {
  276. #pragma unused (window, key, modifiers, refcon)
  277.     return visNoErr;
  278. }
  279.  
  280. /* 
  281.     PlugSettings
  282.  
  283.     Called when user selects your plugin from the Settings submenu. Only used if you have declared that
  284.     you support settings (flagHasSettings is set).
  285.     
  286.     You should display the settings dialog here, or do whateverr you feel like. =) Just remember that when user
  287.     chooses settings (s)he expects some interactino with the plugin, like the dialog or like that.
  288. */
  289. OSErr    PlugSettings(WindowPtr window, UInt32* refcon)
  290. {
  291. #pragma unused (window, refcon)
  292.     return visNoErr;
  293. }
  294.  
  295. /* 
  296.     PlugSongStart
  297.  
  298.     Gets called immediately before the song start. You can obtain ID3 information about the track here etc
  299.     if you want -- it won't be changed until next song starts.
  300. */
  301. OSErr    PlugSongStart(WindowPtr, UInt32*)
  302. {
  303.     // we don't do anything.    
  304.     return visNoErr;
  305. }
  306.  
  307. /* 
  308.     PlugSongEnd
  309.  
  310.     Gets called immediately after the song end. It is a good idea to clear/stop any animations at this point
  311.     because you will not receive PlugProcess calls until next song is started -- that can happen in one
  312.     minute/one second or never. You never know. =)
  313. */
  314. OSErr    PlugSongEnd(WindowPtr window, UInt32* refcon)
  315. {
  316. #pragma unused (window, refcon)
  317.  
  318.     // just reset the bars and draw them.
  319.     
  320.     left = 0;
  321.     right = 0;
  322.     
  323.     DrawMeters(window);
  324.     
  325.     return visNoErr;
  326. }
  327.  
  328. #pragma mark -
  329.  
  330. // The following two functions are examples of analyzing the data stream.
  331. void ProcessMP3(short chan, const double* stream)
  332. {
  333.     int z;
  334.     
  335.     double sum1 = 0.0;
  336.     double sum2 = 0.0;
  337.         
  338.     if (chan == 2)
  339.     {
  340.         for (z = 1; z < 20; z ++)
  341.           sum1 += stream[z];
  342.         for (z = 1; z < 20; z ++)
  343.           sum2 += stream[32*18+z];
  344.     } else {
  345.         for (z = 1; z < 20; z ++)
  346.          sum1 += stream[z];
  347.         sum2 = sum1;
  348.     }
  349.     
  350.     sum1 = ABS(sum1) * 20.0;
  351.     sum1 = MIN(20.0, MAX(0.0, sum1));
  352.     left = MAX(left, sum1);
  353.  
  354.     sum2 = ABS(sum2) * 20.0;
  355.     sum2 = MIN(20.0, MAX(0.0, sum2));
  356.     right = MAX(right, sum2);
  357. }
  358.  
  359. void ProcessMP2(short chan, const double* stream)
  360. {
  361.     short z;
  362.     
  363.     double sum1 = 0.0;
  364.     double sum2 = 0.0;
  365.         
  366.         if (chan == 2)
  367.         {
  368.             for (z = 0; z < 20; z ++)
  369.               sum1 += stream[z];
  370.             for (z = 0; z < 20; z ++)
  371.               sum2 += stream[32*36+z];
  372.         } 
  373.         else 
  374.         {
  375.             for (z = 0; z < 20; z ++)
  376.               sum1 += stream[z];
  377.             sum2 = sum1;
  378.         }
  379.         
  380.     sum1 = ABS(sum1) * 10;
  381.     sum1 = MIN(20.0, MAX(0.0, sum1));
  382.     left = MAX(left, sum1);
  383.  
  384.     sum2 = ABS(sum2) * 10;
  385.     sum2 = MIN(20.0, MAX(0.0, sum2));
  386.     right = MAX(right, sum2);
  387. }
  388.  
  389. // And this is a really simple function that draws the meter bars.
  390. void DrawMeters(WindowPtr wind)
  391. {
  392.     GrafPtr    oldPort;
  393.     unsigned short pos;
  394.     
  395.     GetPort(&oldPort);
  396.     SetPort(wind);
  397.     EraseRect(&wind->portRect);
  398.     ForeColor(greenColor);
  399.     MoveTo(0,60);
  400.     Line(0, -(left * 3));
  401.     MoveTo(5, 60);
  402.     Line(0, -(right * 3));
  403.     ForeColor(redColor);
  404.         
  405.     // call the callback to find out current song position. If it returns an error, we are probably isn't
  406.     // playing a song. Just ignore it and set the position to 0. You can do more extended error checking,
  407.     // depending on your needs.
  408.     
  409.     if (gPlugInfo.callbacks->getCurrentPosProc(&pos) != visNoErr)
  410.         pos = 0;
  411.     
  412.     MoveTo(2, 0);
  413.     Line(0, pos * 0.6);
  414.  
  415.     SetPort(oldPort);
  416. }
  417.